using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Ink;

namespace ScrumTable.UI.View.Adorners
{
    public class InkRotationAdorner : Adorner
    {

        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------
        private readonly Thumb _rotateHandle;
        private readonly Path _outline;
        private readonly VisualCollection _visualChildren;
        private Point _center;
        private double _lastAngle;
        private RotateTransform _rotation;
        private const int Handlemargin = 10;
        private Rect _strokeBounds = Rect.Empty;

        private readonly StrokeCollection _adornedStrokes = new StrokeCollection();

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        // Override the VisualChildrenCount and 
        // GetVisualChild properties to interface with 
        // the adorner's visual collection.
        protected override int VisualChildrenCount
        {
            get { return _visualChildren.Count; }
        }

        protected override Visual GetVisualChild(int index)
        {
            return _visualChildren[index];
        }
        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        
        public InkRotationAdorner(UIElement adornedElement, StrokeCollection strokes)
            : base(adornedElement)
        {
            _adornedStrokes = strokes;

            _visualChildren = new VisualCollection(this);
            _rotateHandle = new Thumb();
            _rotateHandle.Cursor = Cursors.SizeNWSE;
            _rotateHandle.Width = 20;
            _rotateHandle.Height = 20;
            _rotateHandle.Background = Brushes.Blue;

            _rotateHandle.DragDelta += OnRotateHandleDragDelta;
            _rotateHandle.DragCompleted += OnRotateHandleDragCompleted;

            _outline = new Path {Stroke = Brushes.Blue, StrokeThickness = 1};

            _visualChildren.Add(_outline);
            _visualChildren.Add(_rotateHandle);

            _strokeBounds = _adornedStrokes.GetBounds();
           
        }

        
        #endregion


        #region Methods
        
        public void InvalidateStrokeCollectionBounds()
        {
            _strokeBounds = _adornedStrokes.GetBounds();
            _center = new Point(_strokeBounds.X + _strokeBounds.Width / 2, _strokeBounds.Y + _strokeBounds.Height / 2);
            if (_rotation != null)
            {
                _rotation = new RotateTransform(_rotation.Angle, _center.X, _center.Y);
                _outline.RenderTransform = _rotation;
            }
            InvalidateArrange();
        }


        /// <summary>
        /// Draw the rotation handle and the outline of
        /// the element.
        /// </summary>
        /// <param name="finalSize"></param>
        /// <returns></returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            if (_strokeBounds.IsEmpty)
            {
                return finalSize;
            }

            _center = new Point(_strokeBounds.X + _strokeBounds.Width / 2,
                               _strokeBounds.Y + _strokeBounds.Height / 2);

            Rect handleRect = new Rect(_strokeBounds.X,_strokeBounds.Y - (_strokeBounds.Height / 2 +Handlemargin),_strokeBounds.Width, _strokeBounds.Height);
            handleRect.Inflate(5,5);

            if (_rotation != null)
            {
                handleRect.Transform(_rotation.Value);
            }

            // Draws the thumb and the rectangle around the strokes.
            _rotateHandle.Arrange(handleRect);
            _outline.Data = new RectangleGeometry(_strokeBounds);
            _outline.Arrange(new Rect(finalSize));
            return finalSize;
        }
         #endregion

        #region Events


        
        /// <summary>
        /// Rotates the rectangle representing the
        /// strokes' bounds as the user drags the
        /// Thumb.
        /// </summary>
        void OnRotateHandleDragDelta(object sender, DragDeltaEventArgs e)
        {
            //Find the angle of which to rotate the shape.  Use the right
            //triangle that uses the center and the mouse's position as vertices
            //for the hypotonus.

            Point pos = Mouse.GetPosition(this);

            double deltaX = pos.X - _center.X;
            double deltaY = pos.Y - _center.Y;

            if (deltaY.Equals(0))
            {
                return;
            }

            double tan = deltaX / deltaY;
            double angle = Math.Atan(tan);

            // Convert to degrees.
            angle = angle * 180 / Math.PI;

            // If the mouse crosses the vertical center, 
            // find the complementary angle.
            if (deltaY > 0)
            {
                angle = 180 - Math.Abs(angle);
            }

            // Rotate left if the mouse moves left and right
            // if the mouse moves right.
            if (deltaX < 0)
            {
                angle = -Math.Abs(angle);
            }
            else
            {
                angle = Math.Abs(angle);
            }

            if (Double.IsNaN(angle))
            {
                return;
            }

            // Apply the rotation to the strokes' outline.
            _rotation = new RotateTransform(angle, _center.X, _center.Y);
            _outline.RenderTransform = _rotation;
        }

        /// <summary>
        /// Rotates the strokes to the same angle as outline.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnRotateHandleDragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (_rotation == null)
            {
                return;
            }

            // Rotate the strokes to match the new angle.
            Matrix mat = new Matrix();
            mat.RotateAt(_rotation.Angle - _lastAngle, _center.X, _center.Y);
            _adornedStrokes.Transform(mat, true);

            // Save the angle of the last rotation.
            _lastAngle = _rotation.Angle;

            // Redraw rotateHandle.
            this.InvalidateArrange();
        }

        #endregion

    }
}
